﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

namespace Util.Net.Http
{
    public class HttpClient
    {
        public static readonly int DefaultTimeout = 30000;

        public string Accept { get; set; }
        public string AcceptEncoding { get; set; }
        public string AcceptLanguage { get; set; }
        public string CacheControl { get; set; }
        public string UserAgent { get; set; }
        public bool KeepAlive { get; set; }
        public int Timeout { get; set; }
        public string Referer { get; set; }
        public Dictionary<string, string> OtherHeaders { get; private set; }
        private WebProxy proxy;
        public Encoding ResponseEncoding { get; set; }
        public WebProxy Proxy
        {
            get { return proxy; }
            set
            {
                this.proxy = value;
                this.UseDefaultCredentials = value != null;
            }
        }
        public bool UseDefaultCredentials { get; private set; }
        public CookieContainer CookieContainer { get; set; }
        public bool AllowAutoRedirect { get; set; }
        public string ContentType { get; set; }

        public HttpClient()
        {
            this.KeepAlive = true;
            this.Timeout = DefaultTimeout;
            this.Proxy = null;
            this.CookieContainer = new CookieContainer();
            this.OtherHeaders = new Dictionary<string, string>();
            this.AllowAutoRedirect = false;
            this.Referer = null;
        }

        public static HttpClient CreateWebHttpClient()
        {
            HttpClient client = new HttpClient();
            client.Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            client.CacheControl = "max-age=0";
            client.UserAgent = @"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";
            client.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
            return client;
        }

        public static HttpClient CreateMobHttpClient()
        {
            HttpClient client = new HttpClient();
            client.Accept = @"application/xml,application/vnd.wap.xhtml+xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,text/vnd.wap.wml;q=0.6,*/*;q=0.5,UC/145,plugin/1,alipay/en";
            client.AcceptEncoding = "gzip";
            client.AcceptLanguage = "zh-cn";
            client.UserAgent = "Mozilla/5.0 (Linux; U; Android 4.0.4; zh-CN; IM-A760S Build/IMM76D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.4.2.365 U3/0.8.0 Mobile Safari/533.1";
            client.OtherHeaders.Add("X-UCBrowser-UA", @"pf(Linux);la(zh-CN);re(AppleWebKit/533.1 (KHTML, like Gecko));dv(IM-A760S Build/IMM76D);pr(UCBrowser/9.4.2.365);ov(Android 4.0.4);pi(480*762);ss(480*762);up(U3/0.8.0);er(U);bt(UM);pm(1);bv(1);nm(0);im(0);sr(0);nt(2)");
            return client;
        }

        private void BuildRequest(HttpWebRequest request)
        {
            if (!string.IsNullOrWhiteSpace(this.AcceptEncoding))
            {
                request.Headers["Accept-Encoding"] = this.AcceptEncoding;
            }
            if (!string.IsNullOrWhiteSpace(this.AcceptLanguage))
            {
                request.Headers["Accept-Language"] = this.AcceptLanguage;
            }
            if (!string.IsNullOrWhiteSpace(this.CacheControl))
            {
                request.Headers["Cache-Control"] = this.CacheControl;
            }
            if (this.OtherHeaders != null)
            {
                foreach (KeyValuePair<string, string> pair in this.OtherHeaders)
                {
                    request.Headers[pair.Key] = pair.Value;
                }
            }
            request.Accept = Accept;
            request.UserAgent = this.UserAgent;
            request.KeepAlive = this.KeepAlive;
            request.Timeout = this.Timeout;
            request.Proxy = this.Proxy;
            request.Referer = this.Referer;
            request.CookieContainer = this.CookieContainer;
            request.AllowAutoRedirect = this.AllowAutoRedirect;
            request.ContentType = this.ContentType;
        }
        private static Uri BuildGetUrl(string url, Dictionary<string, string> args = null)
        {
            Uri uri = new Uri(url);
            if (args != null)
            {
                List<string> strings = new List<string>();
                foreach (string key in args.Keys)
                {
                    strings.Add(key + "=" + args[key]);
                }
                if (string.IsNullOrEmpty(uri.Query))
                {
                    url += "?" + string.Join("&", strings);
                }
                else
                {
                    url += "&" + string.Join("&", strings);
                }
                uri = new Uri(url);
            }
            return uri;
        }
        public string GetResponseString(string url, Dictionary<string, string> args = null)
        {
            using (HttpWebResponse response = GetResponse(url, args))
            {
                this.PackageCookies(response);
                return ResponseString(response);
            };
        }
        public string ResponseString(HttpWebResponse response)
        {
            Encoding encoding = Encoding.UTF8;
            if (this.ResponseEncoding != null)
            {
                encoding = this.ResponseEncoding;
            }
            //作业网的莫名奇妙的这里返回个ISO编码,事实上文本却是以UTF-8编码的,被玩坏了~,为了作业网注释下这里,以后遇到问题了好回退
            //kzuowen网的编码是ISO-8859-1~~ 
            else if (!string.IsNullOrWhiteSpace(response.CharacterSet))
            {
                encoding = Encoding.GetEncoding(response.CharacterSet);
            }
            using (StreamReader csReader = new StreamReader(response.GetResponseStream(), encoding))
            {
                return csReader.ReadToEnd();
            }
        }
        public HttpWebResponse GetResponse(string url, Dictionary<string, string> args = null)
        {
            Uri uri = BuildGetUrl(url);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri.AbsoluteUri);
            request.Method = "GET";
            BuildRequest(request);
            return (HttpWebResponse)request.GetResponse();
        }
        public MemoryStream GetResponseStream(string url, Dictionary<string, string> args = null)
        {
            using (HttpWebResponse response = GetResponse(url, args))
            {
                long resContentLength = response.ContentLength;                
                MemoryStream memStream = new MemoryStream();
                response.GetResponseStream().CopyTo(memStream);

                if (resContentLength != memStream.Length) 
                {
                    return new MemoryStream();
                }
                return memStream;
            }
        }

        public MemoryStream GetResponseStream(string url, ref string result, Dictionary<string, string> args = null)
        {
            using (HttpWebResponse response = GetResponse(url, args))
            {
                result = response.Headers["Set-Cookie"];
                MemoryStream memStream = new MemoryStream();
                response.GetResponseStream().CopyTo(memStream);
                return memStream;
            }
        }

        private static byte[] BuildPostBytes(Dictionary<string, string> args = null)
        {
            if (args == null || args.Count == 0)
            { return new byte[0]; }
            List<string> strings = new List<string>();
            if (args != null)
            {
                foreach (string key in args.Keys)
                {
                    strings.Add(key + "=" + args[key]);
                }
            }
            return Encoding.UTF8.GetBytes(string.Join("&", strings));
        }

        public string PostResponseString(string url, byte[] bytes)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            
            BuildRequest(request);
            request.ContentLength = bytes.Length;
            using (Stream stream = request.GetRequestStream())
            {
                stream.Write(bytes, 0, bytes.Length);
            }

            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
            {
                this.PackageCookies(response);
                return ResponseString(response);
            }
        }

        public string PostResponseString(string url, Dictionary<string, string> args = null)
        {
            byte[] bytes = BuildPostBytes(args);
            return this.PostResponseString(url, bytes);
        }

        private void PackageCookies(HttpWebResponse response)
        {
            foreach (Cookie c in response.Cookies)
            {
                Cookie cookie = new Cookie(c.Name, c.Value);
                cookie.Domain = c.Domain;
                this.CookieContainer.Add(cookie);
            }
        }
    }
}
